/* 
 * File:   meshaligner.cc
 * Author: boyan
 * 
 * Created on March 18, 2009, 1:13 PM
 */




#include <vector>
#include <algorithm>
#include <assert.h>


#include "MeshAligner.h"
#include "Mesh_grid3d.h"
#include "g3d_mesq.h"
#include "grid3d.h"

#include <MsqVertex.hpp>
#include <MsqError.hpp>
#include <MsqVertex.hpp>
#include <MeshInterface.hpp>
#include <Vector3D.hpp>
#include <ShapeImprovementWrapper.hpp>
#include <LaplacianIQ.hpp>
#include <MeshWriter.hpp>

using Mesquite::Vector3D;
using Mesquite::MsqVertex;
using Mesquite::VertexIterator;
using Mesquite::Mesh;

void MeshAligner::set_domain_dim()
{
    std::vector<Mesh::VertexHandle> h;
    std::vector<unsigned short> dof;
    my_mesh->get_all_vertices(h, err);
    dof.resize(h.size(), 0);
    my_domain->domain_DoF(&h[0], &dof[0], h.size(), err);
    domain_dim = *std::max_element(dof.begin(), dof.end());
}

void MeshAligner::initialize()
{
    if (my_mesh == 0 || my_domain == 0 || chooser == 0)
    {
        MSQ_SETERR(err)(Mesquite::MsqError::INVALID_STATE,
                "Mesh and/or domain and/or chooser not set.\n");
        return;
    }
    if (!chooser->support_dimension(domain_dim))
    {
        MSQ_SETERR(err)(Mesquite::MsqError::INVALID_STATE,
                "chooser does not support dimension of given mesh.\n");
        return;
    }
    initialized = true;
}

void MeshAligner::choose_vertices()
{
    if (!initialized)
    {
        MSQ_SETERR(err)(Mesquite::MsqError::NOT_INITIALIZED);
        return;
    }
    fill_levelset_codes();
    chooser->choose_nodes(my_mesh, my_domain, levelset);
}

void MeshAligner::align()
{
    initialize();
    choose_vertices();
    move_vertices();

    //    return;
    //
    //    grid3d_t *grid=my_mesh->grid3d();
    //    for(int i=0; i<grid->NN; i++)
    //        if(levelset[i]==0&&grid->nodes[i].bcond==BC_NON)
    //            grid->nodes[i].bcond=BC_SURF;
    //
    //    my_mesh->write_g3b("before.bin", err);
    //    Mesquite::MeshWriter::write_vtk(my_mesh, "surf_before.vtk", err);
    //    mesq_handle h=mesq_init(grid);
    //    mesq_run_no_wrapper(h);
    //    mesq_done(h);
    //    my_mesh->write_g3b("after.bin", err);
    //    err.clear();
    //    Mesquite::MeshWriter::write_vtk(my_mesh, "surf_after.vtk", err);
    //
    //    return;
    //
    //    Mesh_surf3d my_surf(*my_mesh->grid3d(), levelset);
    //    Mesquite::ShapeImprovementWrapper wrapper(err);
    //    Mesquite::LaplacianIQ lap;
    //
    //
    //
    //    my_mesh->write_g3b("before.bin", err);
    //    Mesquite::MeshWriter::write_vtk(my_mesh, "surf_before.vtk", err);
    //    std::cout<<" running instructions "<<std::endl;
    //    wrapper.run_instructions(&my_surf, my_domain, err);
    //    //    lap.run_instructions(&my_surf, my_domain, err);
    //    my_mesh->write_g3b("after.bin", err);
    //    Mesquite::MeshWriter::write_vtk(my_mesh, "surf_after.vtk", err);
    //
}

void MeshAligner::set_vertices_chooser(VerticesChooser *new_chooser)
{
    Mesquite::MsqPrintError err(std::cout);
    if (new_chooser == 0)
    {
        switch (domain_dim)
        {
        case 2:
            chooser = VerticesChooser::DEFAULT_CHOOSER_2D;
            break;
        case 3:
            chooser = VerticesChooser::DEFAULT_CHOOSER_3D;
            break;
        default:
            MSQ_SETERR(err)(Mesquite::MsqError::INVALID_STATE,
                    "unsipported geometric dimension\n");
        }
    }
    else
        chooser = new_chooser;
}

void MeshAligner::fill_levelset_codes()
{
    VertexIterator *VI = my_mesh->vertex_iterator(err);
    if (err)
        return;
    levelset.clear();
    for (VI->restart(); !VI->is_at_end(); ++(*VI))
    {
        Mesh::VertexHandle vertex = VI->operator *();
        MsqVertex coords;
        my_mesh->vertices_get_coordinates(&vertex, &coords, 1, err);
        if (err)
            break;
        levelset[vertex] = my_domain->levelset_code(vertex, coords);
    }
    delete VI;
    return;
}

void MeshAligner::move_vertices()
{
    VertexIterator *VI = my_mesh->vertex_iterator(err);
    if (err)
        return;
    for (VI->restart(); !VI->is_at_end(); VI->operator ++())
    {
        Mesh::VertexHandle vertex = VI->operator *();
        if (levelset[vertex] == ls_onsurf)
        { // move node
            MsqVertex pos;
            my_mesh->vertices_get_coordinates(&vertex, &pos, 1, err);
            my_domain->snap_to(vertex, pos);
            my_mesh->vertex_set_coordinates(vertex, pos, err);
        }
    }
    delete VI;
    return;
}

